package com.dingding.gmall.auth.controller;

import com.alibaba.fastjson.JSON;
import com.dingding.gmall.common.constant.QQAuthConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * @program: gmall
 * @description:
 * @author: liugongding
 * @create: 2021-07-28 10:48
 **/

@Slf4j
@Controller
@RequestMapping(value = "/api/qq")
public class QQApiController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/login")
    public void qqlogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String responseType = QQAuthConstant.RESPONSE_TYPE;
        String clientId = QQAuthConstant.APP_ID;
        String state = QQAuthConstant.STATE;
        String redirectUri = QQAuthConstant.REDIRECT_URI;
        String url = String.format(QQAuthConstant.GET_AUTHORIZATION_CODE_URI + "?response_type=%s&client_id=%s&redirect_uri=%s&state=%s",
                responseType, clientId, redirectUri, state);
        request.getSession().setAttribute("state", state);
        response.sendRedirect(url);
    }

    /**
     * 用户授权后的回调方法
     */
    @ResponseBody
    @RequestMapping("/callback")
    public void callbackHandler(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // 1.获取回调的authorization
        String authorizationCode = request.getParameter("code");
        if (StringUtils.isEmpty(authorizationCode)) {
            throw new RuntimeException("未获取到AuthorizationCode");
        }
        // 2.client端的状态值。用于第三方应用防止CSRF攻击。
        String state = request.getParameter("state");
        if (StringUtils.equals(state, QQAuthConstant.STATE)) {
            throw new RuntimeException("client端的状态值不匹配！");
        }

        // 3.获取accessToken
        String urlForAccessToken = getUrlForAccessToken(authorizationCode);
        String accessToken = getAccessToken(urlForAccessToken);

        // 4.根据accessToken获取openId
        if (StringUtils.isEmpty(accessToken)) {
            throw new RuntimeException("未获取到accessToken");
        }
        String openId = getOpenId(accessToken);

        // 5.根据openid获取用户信息
        if (StringUtils.isEmpty(openId)) {
            throw new RuntimeException("未获取到openId");
        }
        String userInfo = getUserInfo(openId,accessToken);
        // ... 获取到用户信息就可以进行自己的业务逻辑处理了
        log.info("userInfo:{}", userInfo);
    }

    /**
     * 拼接获取accessToken的url
     * @param authorizationCode
     * @return
     * @throws UnsupportedEncodingException
     */
    private String getUrlForAccessToken(String authorizationCode) throws UnsupportedEncodingException {
        String grantType = QQAuthConstant.GRANT_TYPE;
        String clientId = QQAuthConstant.APP_ID;
        String clientSecret = QQAuthConstant.APP_KEY;
        String redirectUri = QQAuthConstant.REDIRECT_URI;
//        此处进行URLEncode会导致无法获取AccessToken
//        String redirectUri = URLEncoder.encode(QQAuthConstant.REDIRECT_URI, "UTF-8");
        String url = String.format(QQAuthConstant.GET_ACCESS_TOKEN_URI +
                        "?grant_type=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s",
                grantType, clientId, clientSecret, authorizationCode, redirectUri);

        return url;
    }

    /**
     * getAccessToken
     * @param urlForAccessToken
     * @return
     */
    private String getAccessToken(String urlForAccessToken) {
        //第一次用服务器发起模拟客户端访问,得到的是包含access_token的字符串,其格式如下
        //access_token=0FFD92ABD1DFD4F5&expires_in=7776000&refresh_token=04CE5D1F1E290B0974C5
        String callbackInfo = restTemplate.getForObject(urlForAccessToken, String.class);
        String[] params = StringUtils.split(callbackInfo,"&");
        String accessToken = "";
        if (params != null && params.length > 0){
            for (String param : params) {
                String[] result = StringUtils.split(param,"=");
                if (StringUtils.equals(result[0], "access_token")) {
                    accessToken = result[1];
                    break;
                }
            }
        }
        return accessToken;
    }

    /**
     * 获取openId
     * @param access_token
     * @return
     * @throws IOException
     */
    private String getOpenId(String access_token) throws IOException {
        String url = String.format(QQAuthConstant.GET_OPEN_ID_URI + "?access_token=%s", access_token);
        //第二次模拟客户端发出请求后得到的是带openid的返回数据,格式如下
        //callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
        String callbackInfo = restTemplate.getForObject(url, String.class);
        Map<String, Object> map = JSON.parseObject(callbackInfo);
        if (CollectionUtils.isEmpty(map)){
            return "";
        }
        return (String) map.get("openid");
    }

    /**
     * 根据openid获取用户信息
     * @param openid
     * @param access_token
     * @return
     */
    private String getUserInfo(String openid,String access_token) {
        String infoUrl = String.format(QQAuthConstant.GET_USER_INFO_URI + "?access_token=%s&oauth_consumer_key=%s&openid=%ss",
                access_token, QQAuthConstant.APP_ID, openid);
        String userInfo = restTemplate.getForObject(infoUrl, String.class);
        return userInfo;
    }

    public static void main(String[] args) {
        String str = "{\"0\":\"zhangsan\",\"1\":\"lisi\",\"2\":\"wangwu\",\"3\":\"maliu\"}";
        Map<String, Object> mapTypes = JSON.parseObject(str);
        System.out.println(mapTypes.get("0"));
    }

}
